This week’s data is on United Nations votes, from Harvard’s Database.

library(tidyverse)

unvotes <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/unvotes.csv')
roll_calls <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/roll_calls.csv')
issues <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/issues.csv')

Let’s take a look at each one.

head(unvotes)
head(issues)
head(roll_calls)

Interesting. unvotes includes data on the RCID (roll call ID), looking at each country’s vote for each roll call and their vote. It is accompanied by a two-character country code for each country.

issues has RCID, a shortname for the issue, and the issue itself, such as “Palestinian conflict.”

roll_calls has a session number, an indicator for whether the vote was classified as “important” by the US State department report, a date of the vote, resolution code, whether an amendment, a paragraph, and accompanied by a short description and a long description. Both amend and para are coded only until 1985, and all variables after importantVote apparently begin after session 39, even though there is data before then.

Let’s skim.

skimr::skim(roll_calls)
-- Data Summary ------------------------
                           Values    
Name                       roll_calls
Number of rows             6202      
Number of columns          9         
_______________________              
Column type frequency:               
  character                3         
  Date                     1         
  numeric                  5         
________________________             
Group variables            None      

-- Variable type: character --------------------------------------------------
# A tibble: 3 x 8
  skim_variable n_missing complete_rate   min   max empty n_unique whitespace
* <chr>             <int>         <dbl> <int> <int> <int>    <int>      <int>
1 unres               159         0.974     4    14     0     5702          0
2 short               573         0.908     3   350     0     2018          0
3 descr                 1         1.00      1  1494     0     4524          0

-- Variable type: Date -------------------------------------------------------
# A tibble: 1 x 7
  skim_variable n_missing complete_rate min        max        median    
* <chr>             <int>         <dbl> <date>     <date>     <date>    
1 date                  0             1 1946-01-01 2019-12-27 1986-12-05
  n_unique
*    <int>
1      863

-- Variable type: numeric ----------------------------------------------------
# A tibble: 5 x 11
  skim_variable n_missing complete_rate      mean       sd    p0   p25   p50
* <chr>             <int>         <dbl>     <dbl>    <dbl> <dbl> <dbl> <dbl>
1 rcid                  0         1     3193.     1978.        3 1553. 3104.
2 session               0         1       41.7      19.4       1   28    41 
3 importantvote       604         0.903    0.0734    0.261     0    0     0 
4 amend              3334         0.462    0.108     0.311     0    0     0 
5 para               2994         0.517    0.309     0.462     0    0     0 
    p75  p100 hist 
* <dbl> <dbl> <chr>
1 4669.  9147 ▇▇▇▃▁
2   58     74 ▃▅▇▆▆
3    0      1 ▇▁▁▁▁
4    0      1 ▇▁▁▁▁
5    1      1 ▇▁▁▁▃

As expected, amend and para have a lot of missing data, and the others have only a little (roughly 10% for most incomplete variables).

Votes include “Yes”, “No”, and “Abstain.”

I think, off the top of my head, a network analysis would be interesting here. For a given decade, are there groups of Yeses and Nos? In other words, look at the network of countries who vote Yes on the same issues. If anyone has already done this, I don’t care. It’ll still be interesting. Time to review my networking knowledge…

Also, there are a few ways countries can vote together. On the one hand, they can vote on similar issues similarly. On the other hand, they can vote often together regardless of the issue. The latter is what I’m interested in. That is, I’m interested in large overlaps of issues between countries to identify maybe political influences.

Network Specification

The network will have to connect two countries if their votes are the same on an issue. Let’s do this for a single issue, then I can try to generalize for several issues across some time span.

net <- unvotes %>% 
  filter(rcid == 4807L)

net <- net %>% 
  left_join(issues, by = "rcid")

Here is a randomly selected RCID for a vote on Human Rights. It features a goodly number of countries (183) and a variety of votes.

There should be one observation for each country-to-country pair. That means a full self-join on country, filtered by vote equality.

full_net <- net %>% 
  group_by_all() %>% 
  summarize(other_country = net$country, .groups = "drop") %>% 
  left_join(select(net, country, vote), by = c("other_country" = "country")) %>% 
  rename(vote = vote.x, other_vote = vote.y) %>% 
  filter(vote == other_vote & country != other_country) %>% 
  select(country, other_country, vote, everything())

full_net

There might be an easier way to do this, but it works well enough. Now full_net is the makings of a full bidirectional graph. There are 12,357 connections among 183 countries.

library(igraph)
library(tidygraph)
library(ggraph)

net_graph <- graph_from_data_frame(full_net, directed = TRUE, vertices = select(net, country, everything()))

net_graph
IGRAPH a48e2c6 DN-- 183 12174 -- 
+ attr: name (v/c), rcid (v/n), country_code (v/c), vote (v/c),
| short_name (v/c), issue (v/c), vote (e/c), rcid (e/n), country_code
| (e/c), short_name (e/c), issue (e/c), other_vote (e/c)
+ edges from a48e2c6 (vertex names):
 [1] Afghanistan->United States  Afghanistan->Canada        
 [3] Afghanistan->Bahamas        Afghanistan->Grenada       
 [5] Afghanistan->Honduras       Afghanistan->El Salvador   
 [7] Afghanistan->Costa Rica     Afghanistan->Peru          
 [9] Afghanistan->Paraguay       Afghanistan->Chile         
[11] Afghanistan->Argentina      Afghanistan->Uruguay       
+ ... omitted several edges

Now, plotting this might be dangerous, so let’s do only a reduced dataset.

# full_net <- net %>% 
#   slice_sample(n = 30) %>% 
#   group_by_all() %>% 
#   summarize(other_country = net$country, .groups = "drop") %>% 
#   left_join(select(net, country, vote), by = c("other_country" = "country")) %>% 
#   rename(vote = vote.x, other_vote = vote.y) %>% 
#   filter(vote == other_vote & country != other_country) %>% 
#   select(country, other_country, vote, everything())
# 
# net_graph <- graph_from_data_frame(full_net, directed = TRUE, vertices = select(net, country, everything()))

ggraph(net_graph) + 
  geom_node_point() + 
  geom_edge_diagonal()

Messy, but probably correct? Its hard to see how many points are in the center of each cluster, but theoretically, there are a total of 30 dots all connected to the outer (not included in LHS) countries. This is 2,187 observations. Let’s do the whole thing.

It’s hairy. I rendered it with base graphics so I could blow it up. I think this is not necessarily the way to go with this graphic. Ways I can reduce it:

Here’s how I’m going to shake it out. Countries will include:

These are not exactly randomly chosen, but are supposed to be wide enough to be interesting.

Maybe instead I’ll do only OECD countries, of which there are 37. I’ll need a list of them.

# Vector of OECD countries
oecd <- readr::read_csv('oecd.csv') %>% 
  pull(LOCATION) %>% unique() %>% 
  countrycode::countrycode("iso3c", "iso2c")

-- Column specification ------------------------------------------------------
cols(
  LOCATION = col_character(),
  INDICATOR = col_character(),
  SUBJECT = col_character(),
  MEASURE = col_character(),
  FREQUENCY = col_character(),
  TIME = col_character(),
  Value = col_double(),
  `Flag Codes` = col_character()
)

Some values were not matched unambiguously: EA19, EU27_2020, G-20, G-7, OECD, OECDE

With this data I can make the reduction easily.

net <- filter(net, country_code %in% oecd)

full_net <- net %>%
  group_by_all() %>%
  summarize(other_country = net$country, .groups = "drop") %>%
  left_join(select(net, country, vote), by = c("other_country" = "country")) %>%
  rename(vote = vote.x, other_vote = vote.y) %>%
  filter(vote == other_vote & country != other_country) %>%
  select(country, other_country, vote, everything())

net_graph <- graph_from_data_frame(full_net, directed = TRUE, vertices = select(net, country, everything()))

Now there are only 1,514 edges for a single issue. This will probably remain constant. I’m not sure about an efficent way to ensure that each edge appears only once, so let me think about this problem before selecting more roll calls.

One edge per pair

Assume the countries are alphabetically arranged. I need a dataset where each country’s edges appear only once. For that, I will need to do something like this: append to each country all other countries that come after it. That would be easy for a single issue, but when you have multiple issues that problem gets a little hairier.

I need a better way to expand the dataset with all combinations of countries that come after a certain country. One way is with a loop, but that’ll be a little messy here. Ideally, I could have a non-destructive way to test this.

full_net <- net %>%
  group_by_all() %>%
  summarize(other_country = net$country[net$country > country], .groups = "drop") %>%
  left_join(select(net, country, vote), by = c("other_country" = "country")) %>%
  rename(vote = vote.x, other_vote = vote.y) %>%
  filter(vote == other_vote & country != other_country) %>%
  select(country, other_country, vote, everything())

net_graph <- graph_from_data_frame(full_net, directed = TRUE, vertices = select(net, country, everything()))

Whew, thanks to alphabetization being sortable, this was a cinch. Nice. I reduced the number of edges from 1,514 to 757.

Code so far

library(tidyverse)
library(igraph)
library(tidygraph)
library(ggraph)
library(countrycode)

# Get data -------
unvotes <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/unvotes.csv')
roll_calls <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/roll_calls.csv')
issues <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-03-23/issues.csv')


# Make net dataset --------
net <- unvotes %>% 
  filter(rcid == 4807L) %>% 
  left_join(issues, by = "rcid")

# Vector of OECD countries in ISO-2 character
oecd <- read_csv('oecd.csv') %>% 
  pull(LOCATION) %>% unique() %>% 
  countrycode("iso3c", "iso2c")

oecd <- oecd[!is.na(oecd)]

net <- filter(net, country_code %in% oecd)


# undirected graph data --------
full_net <- net %>%
  group_by_all() %>%
  summarize(other_country = net$country[net$country > country], 
            .groups = "drop") %>%
  left_join(select(net, country, vote), by = c("other_country" = "country")) %>%
  rename(vote = vote.x, other_vote = vote.y) %>%
  filter(vote == other_vote & country != other_country) %>%
  select(country, other_country, vote, everything())

net_graph <- graph_from_data_frame(full_net, directed = TRUE, vertices = select(net, country, everything()))

Selection of Issues

I would like some uniformity, both to limit the scope and make the data more valid. So, what should be included? Probably all votes that are considered “Important” by the US State Department in a given year. I can facet without affecting the layout of the graph, which will help visualize changes over time. Let’s do all important issues in the first year of each decade from 1950 to 2000.

The “important” flag isn’t applied until 1983, so I would only get 1990 and 2000 (N = 3000). Instead, I’m going to try using all important votes since 1983, which as N = 88,679. Probably, this is way to many, but it’ll reduce a bit as it goes on. For now, I’ll reduce it further to half decades since 1980.

decades <- seq(1950, 2000, 10)

net <- unvotes %>% 
  left_join(select(roll_calls, rcid, importantvote, date), by = 'rcid') %>% 
  mutate(year = lubridate::year(date)) %>% 
  filter(year %in% 2010 & 
           importantvote == 1) %>% 
  left_join(issues, by = "rcid")


# Vector of OECD countries in ISO-2 character
oecd <- read_csv('oecd.csv') %>% 
  pull(LOCATION) %>% unique() %>% 
  countrycode("iso3c", "iso2c")

-- Column specification -------------------------------------------------------
cols(
  LOCATION = col_character(),
  INDICATOR = col_character(),
  SUBJECT = col_character(),
  MEASURE = col_character(),
  FREQUENCY = col_character(),
  TIME = col_character(),
  Value = col_double(),
  `Flag Codes` = col_character()
)

Some values were not matched unambiguously: EA19, EU27_2020, G-20, G-7, OECD, OECDE
oecd <- oecd[!is.na(oecd)]

net <- filter(net, country_code %in% oecd)


# undirected graph data --------
full_net <- net %>%
  group_by_all() %>%
  summarize(other_country = net$country[net$country > country], 
            other_rcid = net$rcid[net$country > country],
            .groups = "drop") %>%
  left_join(select(net, country, rcid, vote), by = c("other_country" = "country", "other_rcid" = "rcid")) %>%
  rename(vote = vote.x, other_vote = vote.y) %>%
  filter(vote == other_vote) %>%
  select(country, other_country, vote, everything())

If I try to graph this, I’ll have to graph more than 9 million edges. That’s going to take a very long time.

Let’s reduce again. Maybe first year of decades starting with 1990; that’s only 4 years, instead of the current 20 or so. 1 million edges. That’s still a large number of edges, so let me reduce it again to a single year, just so I can see what it looks like. There should be roughly 250,000 observations. Spot on! Let’s turn this into a graph. I chose 2010, for no reason.

net_vertices <- net %>% 
  filter(rcid == 5049) %>% 
  select(country, country_code)

net_graph <- graph_from_data_frame(full_net, directed = FALSE, vertices = net_vertices)

# ggraph(net_graph, layout = "stress") + 
#   geom_edge_diagonal(aes(edge_colour = vote))

Oof I got a memory allocation error. Calculating the stress layout must be pretty memory consuming, plus I have a lot of stuff in here as it is. But still, I’m shocked, because my largest dataset is 27 MB. If I’m going to free up 96 MB, well I just don’t have it. I gues I need even fewer edges.

Okay, the content of the votes is next. What are the different categories? Which are likely to have some variation?

janitor::tabyl(issues, issue)
                                issue    n   percent
         Arms control and disarmament 1092 0.1900783
                          Colonialism  957 0.1665796
                 Economic development  765 0.1331593
                         Human rights 1015 0.1766754
 Nuclear weapons and nuclear material  855 0.1488251
                 Palestinian conflict 1061 0.1846823

Surprisingly, “Human rights” is one of the more controversial.

net <- unvotes %>% 
  left_join(select(roll_calls, rcid, importantvote, date), by = 'rcid') %>% 
  mutate(year = lubridate::year(date)) %>% 
  left_join(issues, by = "rcid") %>% 
  filter(year %in% seq(1980, 2020, 10) & 
         importantvote == 1 & 
         issue == "Human rights")


# Vector of OECD countries in ISO-2 character
oecd <- read_csv('oecd.csv') %>% 
  pull(LOCATION) %>% unique() %>% 
  countrycode("iso3c", "iso2c")

oecd <- oecd[!is.na(oecd)]

net <- filter(net, country_code %in% oecd)


# undirected graph data --------
full_net <- net %>%
  group_by_all() %>%
  summarize(other_country = net$country[net$country > country & net$rcid == rcid], 
            other_rcid = net$rcid[net$country > country & net$rcid == rcid],
            .groups = "drop") %>%
  left_join(select(net, country, rcid, vote), by = c("other_country" = "country", "other_rcid" = "rcid")) %>%
  rename(vote = vote.x, other_vote = vote.y) %>%
  filter(vote == other_vote) %>%
  select(country, other_country, vote, everything())

net_vertices <- net %>% 
  select(country, country_code) %>% unique()

net_graph <- graph_from_data_frame(full_net, directed = FALSE, vertices = net_vertices)

ggraph(net_graph, layout = 'stress') + 
  geom_edge_diagonal(aes(edge_colour = vote))

There are now 16 votes across 1990-2020 dealing with human rights. There were 609 votes cast among 38-ish countries, and still I’m getting too many observations. And the reason is obvious. I’m using all the data every time. Oops. Okay, observations reduced to only 8000, which makes sense.

That’s I’ll I have time for this morning. Hopefully tomorrow I can make a little more progress.


Charlie Gallagher, 2021

LS0tDQp0aXRsZTogIlVOIFZvdGVzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KVGhpcyB3ZWVrJ3MgZGF0YSBpcyBvbiBVbml0ZWQgTmF0aW9ucyB2b3RlcywgZnJvbSBIYXJ2YXJkJ3MgRGF0YWJhc2UuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCnVudm90ZXMgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMy0yMy91bnZvdGVzLmNzdicpDQpyb2xsX2NhbGxzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDMtMjMvcm9sbF9jYWxscy5jc3YnKQ0KaXNzdWVzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDMtMjMvaXNzdWVzLmNzdicpDQpgYGANCg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBlYWNoIG9uZS4gDQoNCmBgYHtyfQ0KaGVhZCh1bnZvdGVzKQ0KaGVhZChpc3N1ZXMpDQpoZWFkKHJvbGxfY2FsbHMpDQpgYGANCg0KSW50ZXJlc3RpbmcuIGB1bnZvdGVzYCBpbmNsdWRlcyBkYXRhIG9uIHRoZSBSQ0lEIChyb2xsIGNhbGwgSUQpLCBsb29raW5nIGF0IGVhY2ggY291bnRyeSdzIHZvdGUgZm9yIGVhY2ggcm9sbCBjYWxsIGFuZCB0aGVpciB2b3RlLiBJdCBpcyBhY2NvbXBhbmllZCBieSBhIHR3by1jaGFyYWN0ZXIgY291bnRyeSBjb2RlIGZvciBlYWNoIGNvdW50cnkuIA0KDQpgaXNzdWVzYCBoYXMgUkNJRCwgYSBzaG9ydG5hbWUgZm9yIHRoZSBpc3N1ZSwgYW5kIHRoZSBpc3N1ZSBpdHNlbGYsIHN1Y2ggYXMgIlBhbGVzdGluaWFuIGNvbmZsaWN0LiINCg0KYHJvbGxfY2FsbHNgIGhhcyBhIHNlc3Npb24gbnVtYmVyLCBhbiBpbmRpY2F0b3IgZm9yIHdoZXRoZXIgdGhlIHZvdGUgd2FzIGNsYXNzaWZpZWQgYXMgImltcG9ydGFudCIgYnkgdGhlIFVTIFN0YXRlIGRlcGFydG1lbnQgcmVwb3J0LCBhIGRhdGUgb2YgdGhlIHZvdGUsIHJlc29sdXRpb24gY29kZSwgd2hldGhlciBhbiBhbWVuZG1lbnQsIGEgcGFyYWdyYXBoLCBhbmQgYWNjb21wYW5pZWQgYnkgYSBzaG9ydCBkZXNjcmlwdGlvbiBhbmQgYSBsb25nIGRlc2NyaXB0aW9uLiBCb3RoIGBhbWVuZGAgYW5kIGBwYXJhYCBhcmUgY29kZWQgb25seSB1bnRpbCAxOTg1LCBhbmQgYWxsIHZhcmlhYmxlcyBhZnRlciBgaW1wb3J0YW50Vm90ZWAgYXBwYXJlbnRseSBiZWdpbiBhZnRlciBzZXNzaW9uIDM5LCBldmVuIHRob3VnaCB0aGVyZSBpcyBkYXRhIGJlZm9yZSB0aGVuLiANCg0KTGV0J3Mgc2tpbS4gDQoNCmBgYHtyfQ0Kc2tpbXI6OnNraW0ocm9sbF9jYWxscykNCmBgYA0KDQpBcyBleHBlY3RlZCwgYGFtZW5kYCBhbmQgYHBhcmFgIGhhdmUgYSBsb3Qgb2YgbWlzc2luZyBkYXRhLCBhbmQgdGhlIG90aGVycyBoYXZlIG9ubHkgYSBsaXR0bGUgKHJvdWdobHkgMTAlIGZvciBtb3N0IGluY29tcGxldGUgdmFyaWFibGVzKS4gDQoNClZvdGVzIGluY2x1ZGUgIlllcyIsICJObyIsIGFuZCAiQWJzdGFpbi4iDQoNCkkgdGhpbmssIG9mZiB0aGUgdG9wIG9mIG15IGhlYWQsIGEgbmV0d29yayBhbmFseXNpcyB3b3VsZCBiZSBpbnRlcmVzdGluZyBoZXJlLiBGb3IgYSBnaXZlbiBkZWNhZGUsIGFyZSB0aGVyZSBncm91cHMgb2YgWWVzZXMgYW5kIE5vcz8gSW4gb3RoZXIgd29yZHMsIGxvb2sgYXQgdGhlIG5ldHdvcmsgb2YgY291bnRyaWVzIHdobyB2b3RlIFllcyBvbiB0aGUgc2FtZSBpc3N1ZXMuIElmIGFueW9uZSBoYXMgYWxyZWFkeSBkb25lIHRoaXMsIEkgZG9uJ3QgY2FyZS4gSXQnbGwgc3RpbGwgYmUgaW50ZXJlc3RpbmcuIFRpbWUgdG8gcmV2aWV3IG15IG5ldHdvcmtpbmcga25vd2xlZGdlLi4uDQoNCkFsc28sIHRoZXJlIGFyZSBhIGZldyB3YXlzIGNvdW50cmllcyBjYW4gdm90ZSB0b2dldGhlci4gT24gdGhlIG9uZSBoYW5kLCB0aGV5IGNhbiB2b3RlIG9uIHNpbWlsYXIgaXNzdWVzIHNpbWlsYXJseS4gT24gdGhlIG90aGVyIGhhbmQsIHRoZXkgY2FuIHZvdGUgb2Z0ZW4gdG9nZXRoZXIgcmVnYXJkbGVzcyBvZiB0aGUgaXNzdWUuIFRoZSBsYXR0ZXIgaXMgd2hhdCBJJ20gaW50ZXJlc3RlZCBpbi4gVGhhdCBpcywgSSdtIGludGVyZXN0ZWQgaW4gbGFyZ2Ugb3ZlcmxhcHMgb2YgaXNzdWVzIGJldHdlZW4gY291bnRyaWVzIHRvIGlkZW50aWZ5IG1heWJlIHBvbGl0aWNhbCBpbmZsdWVuY2VzLiANCg0KIyBOZXR3b3JrIFNwZWNpZmljYXRpb24NClRoZSBuZXR3b3JrIHdpbGwgaGF2ZSB0byBjb25uZWN0IHR3byBjb3VudHJpZXMgaWYgdGhlaXIgdm90ZXMgYXJlIHRoZSBzYW1lIG9uIGFuIGlzc3VlLiBMZXQncyBkbyB0aGlzIGZvciBhIHNpbmdsZSBpc3N1ZSwgdGhlbiBJIGNhbiB0cnkgdG8gZ2VuZXJhbGl6ZSBmb3Igc2V2ZXJhbCBpc3N1ZXMgYWNyb3NzIHNvbWUgdGltZSBzcGFuLiANCg0KYGBge3J9DQpuZXQgPC0gdW52b3RlcyAlPiUgDQogIGZpbHRlcihyY2lkID09IDQ4MDdMKQ0KDQpuZXQgPC0gbmV0ICU+JSANCiAgbGVmdF9qb2luKGlzc3VlcywgYnkgPSAicmNpZCIpDQpgYGANCg0KSGVyZSBpcyBhIHJhbmRvbWx5IHNlbGVjdGVkIFJDSUQgZm9yIGEgdm90ZSBvbiBIdW1hbiBSaWdodHMuIEl0IGZlYXR1cmVzIGEgZ29vZGx5IG51bWJlciBvZiBjb3VudHJpZXMgKDE4MykgYW5kIGEgdmFyaWV0eSBvZiB2b3Rlcy4gDQoNClRoZXJlIHNob3VsZCBiZSBvbmUgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY291bnRyeS10by1jb3VudHJ5IHBhaXIuIFRoYXQgbWVhbnMgYSBmdWxsIHNlbGYtam9pbiBvbiBjb3VudHJ5LCBmaWx0ZXJlZCBieSB2b3RlIGVxdWFsaXR5LiANCg0KYGBge3J9DQpmdWxsX25ldCA8LSBuZXQgJT4lIA0KICBncm91cF9ieV9hbGwoKSAlPiUgDQogIHN1bW1hcml6ZShvdGhlcl9jb3VudHJ5ID0gbmV0JGNvdW50cnksIC5ncm91cHMgPSAiZHJvcCIpICU+JSANCiAgbGVmdF9qb2luKHNlbGVjdChuZXQsIGNvdW50cnksIHZvdGUpLCBieSA9IGMoIm90aGVyX2NvdW50cnkiID0gImNvdW50cnkiKSkgJT4lIA0KICByZW5hbWUodm90ZSA9IHZvdGUueCwgb3RoZXJfdm90ZSA9IHZvdGUueSkgJT4lIA0KICBmaWx0ZXIodm90ZSA9PSBvdGhlcl92b3RlICYgY291bnRyeSAhPSBvdGhlcl9jb3VudHJ5KSAlPiUgDQogIHNlbGVjdChjb3VudHJ5LCBvdGhlcl9jb3VudHJ5LCB2b3RlLCBldmVyeXRoaW5nKCkpDQoNCmZ1bGxfbmV0DQpgYGANCg0KVGhlcmUgbWlnaHQgYmUgYW4gZWFzaWVyIHdheSB0byBkbyB0aGlzLCBidXQgaXQgd29ya3Mgd2VsbCBlbm91Z2guIE5vdyBgZnVsbF9uZXRgIGlzIHRoZSBtYWtpbmdzIG9mIGEgZnVsbCBiaWRpcmVjdGlvbmFsIGdyYXBoLiBUaGVyZSBhcmUgMTIsMzU3IGNvbm5lY3Rpb25zIGFtb25nIDE4MyBjb3VudHJpZXMuIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoaWdyYXBoKQ0KbGlicmFyeSh0aWR5Z3JhcGgpDQpsaWJyYXJ5KGdncmFwaCkNCg0KbmV0X2dyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShmdWxsX25ldCwgZGlyZWN0ZWQgPSBUUlVFLCB2ZXJ0aWNlcyA9IHNlbGVjdChuZXQsIGNvdW50cnksIGV2ZXJ5dGhpbmcoKSkpDQoNCm5ldF9ncmFwaA0KYGBgDQoNCg0KTm93LCBwbG90dGluZyB0aGlzIG1pZ2h0IGJlIGRhbmdlcm91cywgc28gbGV0J3MgZG8gb25seSBhIHJlZHVjZWQgZGF0YXNldC4gDQoNCmBgYHtyfQ0KIyBmdWxsX25ldCA8LSBuZXQgJT4lIA0KIyAgIHNsaWNlX3NhbXBsZShuID0gMzApICU+JSANCiMgICBncm91cF9ieV9hbGwoKSAlPiUgDQojICAgc3VtbWFyaXplKG90aGVyX2NvdW50cnkgPSBuZXQkY291bnRyeSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIA0KIyAgIGxlZnRfam9pbihzZWxlY3QobmV0LCBjb3VudHJ5LCB2b3RlKSwgYnkgPSBjKCJvdGhlcl9jb3VudHJ5IiA9ICJjb3VudHJ5IikpICU+JSANCiMgICByZW5hbWUodm90ZSA9IHZvdGUueCwgb3RoZXJfdm90ZSA9IHZvdGUueSkgJT4lIA0KIyAgIGZpbHRlcih2b3RlID09IG90aGVyX3ZvdGUgJiBjb3VudHJ5ICE9IG90aGVyX2NvdW50cnkpICU+JSANCiMgICBzZWxlY3QoY291bnRyeSwgb3RoZXJfY291bnRyeSwgdm90ZSwgZXZlcnl0aGluZygpKQ0KIyANCiMgbmV0X2dyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShmdWxsX25ldCwgZGlyZWN0ZWQgPSBUUlVFLCB2ZXJ0aWNlcyA9IHNlbGVjdChuZXQsIGNvdW50cnksIGV2ZXJ5dGhpbmcoKSkpDQoNCmdncmFwaChuZXRfZ3JhcGgpICsgDQogIGdlb21fbm9kZV9wb2ludCgpICsgDQogIGdlb21fZWRnZV9kaWFnb25hbCgpDQpgYGANCg0KTWVzc3ksIGJ1dCBwcm9iYWJseSBjb3JyZWN0PyBJdHMgaGFyZCB0byBzZWUgaG93IG1hbnkgcG9pbnRzIGFyZSBpbiB0aGUgY2VudGVyIG9mIGVhY2ggY2x1c3RlciwgYnV0IHRoZW9yZXRpY2FsbHksIHRoZXJlIGFyZSBhIHRvdGFsIG9mIDMwIGRvdHMgYWxsIGNvbm5lY3RlZCB0byB0aGUgb3V0ZXIgKG5vdCBpbmNsdWRlZCBpbiBMSFMpIGNvdW50cmllcy4gVGhpcyBpcyAyLDE4NyBvYnNlcnZhdGlvbnMuIExldCdzIGRvIHRoZSB3aG9sZSB0aGluZy4gDQoNCkl0J3MgaGFpcnkuIEkgcmVuZGVyZWQgaXQgd2l0aCBiYXNlIGdyYXBoaWNzIHNvIEkgY291bGQgYmxvdyBpdCB1cC4gSSB0aGluayB0aGlzIGlzIG5vdCBuZWNlc3NhcmlseSB0aGUgd2F5IHRvIGdvIHdpdGggdGhpcyBncmFwaGljLiBXYXlzIEkgY2FuIHJlZHVjZSBpdDogDQoNCi0gcmVkdWNlIG51bWJlciBvZiBjb3VudHJpZXMgY29uc2lkZXJlZA0KLSByZWR1Y2UgdHlwZXMgb2Ygdm90ZXMgY29uc2lkZXJlZA0KLSBHaXZlIGVkZ2VzIGF0dHJpYnV0ZXMgc3VjaCBhcyAibnVtYmVyIG9mIHRpbWVzIHZvdGVkIHRvZ2V0aGVyLCIgYW5kIG1ha2UgDQp0aGF0IHRoZSBlZGdlIGJldHdlZW4gdGhlIHR3byBjb3VudHJpZXMNCi0gS2VlcCBvbmx5IG9uZSBlZGdlIGJldHdlZW4gdHdvIGNvdW50cmllcw0KDQoNCkhlcmUncyBob3cgSSdtIGdvaW5nIHRvIHNoYWtlIGl0IG91dC4gQ291bnRyaWVzIHdpbGwgaW5jbHVkZTogDQoNCi0gRXVyb3BlYW4gVW5pb24NCi0gVW5pdGVkIFN0YXRlcw0KLSBSdXNzaWEsIENoaW5hLCBTYXVkaSBBcmFiaWENCg0KVGhlc2UgYXJlIG5vdCBleGFjdGx5IHJhbmRvbWx5IGNob3NlbiwgYnV0IGFyZSBzdXBwb3NlZCB0byBiZSB3aWRlIGVub3VnaCB0byBiZSBpbnRlcmVzdGluZy4gDQoNCmBgYHtyfQ0KbGlicmFyeShjb3VudHJ5Y29kZSkNCg0KY291bnRyeWNvZGU6OmNvZGVsaXN0DQpgYGANCg0KDQpNYXliZSBpbnN0ZWFkIEknbGwgZG8gb25seSBPRUNEIGNvdW50cmllcywgb2Ygd2hpY2ggdGhlcmUgYXJlIDM3LiBJJ2xsIG5lZWQgYSBsaXN0IG9mIHRoZW0uIA0KDQpgYGB7cn0NCiMgVmVjdG9yIG9mIE9FQ0QgY291bnRyaWVzIGluIElTTy0yIGNoYXJhY3Rlcg0Kb2VjZCA8LSByZWFkcjo6cmVhZF9jc3YoJ29lY2QuY3N2JykgJT4lIA0KICBwdWxsKExPQ0FUSU9OKSAlPiUgdW5pcXVlKCkgJT4lIA0KICBjb3VudHJ5Y29kZTo6Y291bnRyeWNvZGUoImlzbzNjIiwgImlzbzJjIikNCg0Kb2VjZCA8LSBvZWNkWyFpcy5uYShvZWNkKV0NCmBgYA0KDQoNCldpdGggdGhpcyBkYXRhIEkgY2FuIG1ha2UgdGhlIHJlZHVjdGlvbiBlYXNpbHkuIA0KDQpgYGB7cn0NCm5ldCA8LSBmaWx0ZXIobmV0LCBjb3VudHJ5X2NvZGUgJWluJSBvZWNkKQ0KDQpmdWxsX25ldCA8LSBuZXQgJT4lDQogIGdyb3VwX2J5X2FsbCgpICU+JQ0KICBzdW1tYXJpemUob3RoZXJfY291bnRyeSA9IG5ldCRjb3VudHJ5LCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgbGVmdF9qb2luKHNlbGVjdChuZXQsIGNvdW50cnksIHZvdGUpLCBieSA9IGMoIm90aGVyX2NvdW50cnkiID0gImNvdW50cnkiKSkgJT4lDQogIHJlbmFtZSh2b3RlID0gdm90ZS54LCBvdGhlcl92b3RlID0gdm90ZS55KSAlPiUNCiAgZmlsdGVyKHZvdGUgPT0gb3RoZXJfdm90ZSAmIGNvdW50cnkgIT0gb3RoZXJfY291bnRyeSkgJT4lDQogIHNlbGVjdChjb3VudHJ5LCBvdGhlcl9jb3VudHJ5LCB2b3RlLCBldmVyeXRoaW5nKCkpDQoNCm5ldF9ncmFwaCA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZnVsbF9uZXQsIGRpcmVjdGVkID0gVFJVRSwgdmVydGljZXMgPSBzZWxlY3QobmV0LCBjb3VudHJ5LCBldmVyeXRoaW5nKCkpKQ0KYGBgDQoNCk5vdyB0aGVyZSBhcmUgb25seSAxLDUxNCBlZGdlcyBmb3IgYSBzaW5nbGUgaXNzdWUuIFRoaXMgd2lsbCBwcm9iYWJseSByZW1haW4gY29uc3RhbnQuIEknbSBub3Qgc3VyZSBhYm91dCBhbiBlZmZpY2VudCB3YXkgdG8gZW5zdXJlIHRoYXQgZWFjaCBlZGdlIGFwcGVhcnMgb25seSBvbmNlLCBzbyBsZXQgbWUgdGhpbmsgYWJvdXQgdGhpcyBwcm9ibGVtIGJlZm9yZSBzZWxlY3RpbmcgbW9yZSByb2xsIGNhbGxzLg0KDQojIyMgT25lIGVkZ2UgcGVyIHBhaXINCkFzc3VtZSB0aGUgY291bnRyaWVzIGFyZSBhbHBoYWJldGljYWxseSBhcnJhbmdlZC4gSSBuZWVkIGEgZGF0YXNldCB3aGVyZSBlYWNoIGNvdW50cnkncyBlZGdlcyBhcHBlYXIgb25seSBvbmNlLiBGb3IgdGhhdCwgSSB3aWxsIG5lZWQgdG8gZG8gc29tZXRoaW5nIGxpa2UgdGhpczogYXBwZW5kIHRvIGVhY2ggY291bnRyeSBhbGwgb3RoZXIgY291bnRyaWVzIHRoYXQgY29tZSBhZnRlciBpdC4gVGhhdCB3b3VsZCBiZSBlYXN5IGZvciBhIHNpbmdsZSBpc3N1ZSwgYnV0IHdoZW4geW91IGhhdmUgbXVsdGlwbGUgaXNzdWVzIHRoYXQgcHJvYmxlbSBnZXRzIGEgbGl0dGxlIGhhaXJpZXIuIA0KDQpJIG5lZWQgYSBiZXR0ZXIgd2F5IHRvIGV4cGFuZCB0aGUgZGF0YXNldCB3aXRoIGFsbCBjb21iaW5hdGlvbnMgb2YgY291bnRyaWVzIHRoYXQgY29tZSBhZnRlciBhIGNlcnRhaW4gY291bnRyeS4gT25lIHdheSBpcyB3aXRoIGEgbG9vcCwgYnV0IHRoYXQnbGwgYmUgYSBsaXR0bGUgbWVzc3kgaGVyZS4gSWRlYWxseSwgSSBjb3VsZCBoYXZlIGEgbm9uLWRlc3RydWN0aXZlIHdheSB0byB0ZXN0IHRoaXMuIA0KDQpgYGB7cn0NCmZ1bGxfbmV0IDwtIG5ldCAlPiUNCiAgZ3JvdXBfYnlfYWxsKCkgJT4lDQogIHN1bW1hcml6ZShvdGhlcl9jb3VudHJ5ID0gbmV0JGNvdW50cnlbbmV0JGNvdW50cnkgPiBjb3VudHJ5XSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGxlZnRfam9pbihzZWxlY3QobmV0LCBjb3VudHJ5LCB2b3RlKSwgYnkgPSBjKCJvdGhlcl9jb3VudHJ5IiA9ICJjb3VudHJ5IikpICU+JQ0KICByZW5hbWUodm90ZSA9IHZvdGUueCwgb3RoZXJfdm90ZSA9IHZvdGUueSkgJT4lDQogIGZpbHRlcih2b3RlID09IG90aGVyX3ZvdGUgJiBjb3VudHJ5ICE9IG90aGVyX2NvdW50cnkpICU+JQ0KICBzZWxlY3QoY291bnRyeSwgb3RoZXJfY291bnRyeSwgdm90ZSwgZXZlcnl0aGluZygpKQ0KDQpuZXRfZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGZ1bGxfbmV0LCBkaXJlY3RlZCA9IFRSVUUsIHZlcnRpY2VzID0gc2VsZWN0KG5ldCwgY291bnRyeSwgZXZlcnl0aGluZygpKSkNCmBgYA0KDQoNCldoZXcsIHRoYW5rcyB0byBhbHBoYWJldGl6YXRpb24gYmVpbmcgc29ydGFibGUsIHRoaXMgd2FzIGEgY2luY2guIE5pY2UuIEkgcmVkdWNlZCB0aGUgbnVtYmVyIG9mIGVkZ2VzIGZyb20gMSw1MTQgdG8gNzU3LiANCg0KDQojIyMgQ29kZSBzbyBmYXINCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkodGlkeWdyYXBoKQ0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KGNvdW50cnljb2RlKQ0KDQojIEdldCBkYXRhIC0tLS0tLS0NCnVudm90ZXMgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMy0yMy91bnZvdGVzLmNzdicpDQpyb2xsX2NhbGxzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDMtMjMvcm9sbF9jYWxscy5jc3YnKQ0KaXNzdWVzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDMtMjMvaXNzdWVzLmNzdicpDQoNCg0KIyBNYWtlIG5ldCBkYXRhc2V0IC0tLS0tLS0tDQpuZXQgPC0gdW52b3RlcyAlPiUgDQogIGZpbHRlcihyY2lkID09IDQ4MDdMKSAlPiUgDQogIGxlZnRfam9pbihpc3N1ZXMsIGJ5ID0gInJjaWQiKQ0KDQojIFZlY3RvciBvZiBPRUNEIGNvdW50cmllcyBpbiBJU08tMiBjaGFyYWN0ZXINCm9lY2QgPC0gcmVhZF9jc3YoJ29lY2QuY3N2JykgJT4lIA0KICBwdWxsKExPQ0FUSU9OKSAlPiUgdW5pcXVlKCkgJT4lIA0KICBjb3VudHJ5Y29kZSgiaXNvM2MiLCAiaXNvMmMiKQ0KDQpvZWNkIDwtIG9lY2RbIWlzLm5hKG9lY2QpXQ0KDQpuZXQgPC0gZmlsdGVyKG5ldCwgY291bnRyeV9jb2RlICVpbiUgb2VjZCkNCg0KDQojIHVuZGlyZWN0ZWQgZ3JhcGggZGF0YSAtLS0tLS0tLQ0KZnVsbF9uZXQgPC0gbmV0ICU+JQ0KICBncm91cF9ieV9hbGwoKSAlPiUNCiAgc3VtbWFyaXplKG90aGVyX2NvdW50cnkgPSBuZXQkY291bnRyeVtuZXQkY291bnRyeSA+IGNvdW50cnldLCANCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBsZWZ0X2pvaW4oc2VsZWN0KG5ldCwgY291bnRyeSwgdm90ZSksIGJ5ID0gYygib3RoZXJfY291bnRyeSIgPSAiY291bnRyeSIpKSAlPiUNCiAgcmVuYW1lKHZvdGUgPSB2b3RlLngsIG90aGVyX3ZvdGUgPSB2b3RlLnkpICU+JQ0KICBmaWx0ZXIodm90ZSA9PSBvdGhlcl92b3RlICYgY291bnRyeSAhPSBvdGhlcl9jb3VudHJ5KSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIG90aGVyX2NvdW50cnksIHZvdGUsIGV2ZXJ5dGhpbmcoKSkNCg0KbmV0X2dyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShmdWxsX25ldCwgZGlyZWN0ZWQgPSBUUlVFLCB2ZXJ0aWNlcyA9IHNlbGVjdChuZXQsIGNvdW50cnksIGV2ZXJ5dGhpbmcoKSkpDQpgYGANCg0KDQojIyMgU2VsZWN0aW9uIG9mIElzc3Vlcw0KSSB3b3VsZCBsaWtlIHNvbWUgdW5pZm9ybWl0eSwgYm90aCB0byBsaW1pdCB0aGUgc2NvcGUgYW5kIG1ha2UgdGhlIGRhdGEgbW9yZSB2YWxpZC4gU28sIHdoYXQgc2hvdWxkIGJlIGluY2x1ZGVkPyBQcm9iYWJseSBhbGwgdm90ZXMgdGhhdCBhcmUgY29uc2lkZXJlZCAiSW1wb3J0YW50IiBieSB0aGUgVVMgU3RhdGUgRGVwYXJ0bWVudCBpbiBhIGdpdmVuIHllYXIuIEkgY2FuIGZhY2V0IHdpdGhvdXQgYWZmZWN0aW5nIHRoZSBsYXlvdXQgb2YgdGhlIGdyYXBoLCB3aGljaCB3aWxsIGhlbHAgdmlzdWFsaXplIGNoYW5nZXMgb3ZlciB0aW1lLiBMZXQncyBkbyBhbGwgaW1wb3J0YW50IGlzc3VlcyBpbiB0aGUgZmlyc3QgeWVhciBvZiBlYWNoIGRlY2FkZSBmcm9tIDE5NTAgdG8gMjAwMC4gDQoNClRoZSAiaW1wb3J0YW50IiBmbGFnIGlzbid0IGFwcGxpZWQgdW50aWwgMTk4Mywgc28gSSB3b3VsZCBvbmx5IGdldCAxOTkwIGFuZCAyMDAwIChOID0gMzAwMCkuIEluc3RlYWQsIEknbSBnb2luZyB0byB0cnkgdXNpbmcgYWxsIGltcG9ydGFudCB2b3RlcyBzaW5jZSAxOTgzLCB3aGljaCBhcyBOID0gODgsNjc5LiBQcm9iYWJseSwgdGhpcyBpcyB3YXkgdG8gbWFueSwgYnV0IGl0J2xsIHJlZHVjZSBhIGJpdCBhcyBpdCBnb2VzIG9uLiBGb3Igbm93LCBJJ2xsIHJlZHVjZSBpdCBmdXJ0aGVyIHRvIGhhbGYgZGVjYWRlcyBzaW5jZSAxOTgwLiANCg0KYGBge3J9DQpkZWNhZGVzIDwtIHNlcSgxOTUwLCAyMDAwLCAxMCkNCg0KbmV0IDwtIHVudm90ZXMgJT4lIA0KICBsZWZ0X2pvaW4oc2VsZWN0KHJvbGxfY2FsbHMsIHJjaWQsIGltcG9ydGFudHZvdGUsIGRhdGUpLCBieSA9ICdyY2lkJykgJT4lIA0KICBtdXRhdGUoeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihkYXRlKSkgJT4lIA0KICBmaWx0ZXIoeWVhciAlaW4lIDIwMTAgJiANCiAgICAgICAgICAgaW1wb3J0YW50dm90ZSA9PSAxKSAlPiUgDQogIGxlZnRfam9pbihpc3N1ZXMsIGJ5ID0gInJjaWQiKQ0KDQoNCiMgVmVjdG9yIG9mIE9FQ0QgY291bnRyaWVzIGluIElTTy0yIGNoYXJhY3Rlcg0Kb2VjZCA8LSByZWFkX2Nzdignb2VjZC5jc3YnKSAlPiUgDQogIHB1bGwoTE9DQVRJT04pICU+JSB1bmlxdWUoKSAlPiUgDQogIGNvdW50cnljb2RlKCJpc28zYyIsICJpc28yYyIpDQoNCm9lY2QgPC0gb2VjZFshaXMubmEob2VjZCldDQoNCm5ldCA8LSBmaWx0ZXIobmV0LCBjb3VudHJ5X2NvZGUgJWluJSBvZWNkKQ0KDQoNCiMgdW5kaXJlY3RlZCBncmFwaCBkYXRhIC0tLS0tLS0tDQpmdWxsX25ldCA8LSBuZXQgJT4lDQogIGdyb3VwX2J5X2FsbCgpICU+JQ0KICBzdW1tYXJpemUob3RoZXJfY291bnRyeSA9IG5ldCRjb3VudHJ5W25ldCRjb3VudHJ5ID4gY291bnRyeV0sIA0KICAgICAgICAgICAgb3RoZXJfcmNpZCA9IG5ldCRyY2lkW25ldCRjb3VudHJ5ID4gY291bnRyeV0sDQogICAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgbGVmdF9qb2luKHNlbGVjdChuZXQsIGNvdW50cnksIHJjaWQsIHZvdGUpLCBieSA9IGMoIm90aGVyX2NvdW50cnkiID0gImNvdW50cnkiLCAib3RoZXJfcmNpZCIgPSAicmNpZCIpKSAlPiUNCiAgcmVuYW1lKHZvdGUgPSB2b3RlLngsIG90aGVyX3ZvdGUgPSB2b3RlLnkpICU+JQ0KICBmaWx0ZXIodm90ZSA9PSBvdGhlcl92b3RlKSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIG90aGVyX2NvdW50cnksIHZvdGUsIGV2ZXJ5dGhpbmcoKSkNCg0KYGBgDQoNCg0KSWYgSSB0cnkgdG8gZ3JhcGggdGhpcywgSSdsbCBoYXZlIHRvIGdyYXBoIG1vcmUgdGhhbiA5IG1pbGxpb24gZWRnZXMuIFRoYXQncyBnb2luZyB0byB0YWtlIGEgdmVyeSBsb25nIHRpbWUuIA0KDQpMZXQncyByZWR1Y2UgYWdhaW4uIE1heWJlIGZpcnN0IHllYXIgb2YgZGVjYWRlcyBzdGFydGluZyB3aXRoIDE5OTA7IHRoYXQncyBvbmx5IDQgeWVhcnMsIGluc3RlYWQgb2YgdGhlIGN1cnJlbnQgMjAgb3Igc28uIDEgbWlsbGlvbiBlZGdlcy4gVGhhdCdzIHN0aWxsIGEgbGFyZ2UgbnVtYmVyIG9mIGVkZ2VzLCBzbyBsZXQgbWUgcmVkdWNlIGl0IGFnYWluIHRvIGEgc2luZ2xlIHllYXIsIGp1c3Qgc28gSSBjYW4gc2VlIHdoYXQgaXQgbG9va3MgbGlrZS4gVGhlcmUgc2hvdWxkIGJlIHJvdWdobHkgMjUwLDAwMCBvYnNlcnZhdGlvbnMuIFNwb3Qgb24hIExldCdzIHR1cm4gdGhpcyBpbnRvIGEgZ3JhcGguIEkgY2hvc2UgMjAxMCwgZm9yIG5vIHJlYXNvbi4gDQoNCg0KYGBge3J9DQpuZXRfdmVydGljZXMgPC0gbmV0ICU+JSANCiAgZmlsdGVyKHJjaWQgPT0gNTA0OSkgJT4lIA0KICBzZWxlY3QoY291bnRyeSwgY291bnRyeV9jb2RlKQ0KDQpuZXRfZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGZ1bGxfbmV0LCBkaXJlY3RlZCA9IEZBTFNFLCB2ZXJ0aWNlcyA9IG5ldF92ZXJ0aWNlcykNCg0KIyBnZ3JhcGgobmV0X2dyYXBoLCBsYXlvdXQgPSAic3RyZXNzIikgKyANCiMgICBnZW9tX2VkZ2VfZGlhZ29uYWwoYWVzKGVkZ2VfY29sb3VyID0gdm90ZSkpDQpgYGANCg0KT29mIEkgZ290IGEgbWVtb3J5IGFsbG9jYXRpb24gZXJyb3IuIENhbGN1bGF0aW5nIHRoZSBzdHJlc3MgbGF5b3V0IG11c3QgYmUgcHJldHR5IG1lbW9yeSBjb25zdW1pbmcsIHBsdXMgSSBoYXZlIGEgbG90IG9mIHN0dWZmIGluIGhlcmUgYXMgaXQgaXMuIEJ1dCBzdGlsbCwgSSdtIHNob2NrZWQsIGJlY2F1c2UgbXkgbGFyZ2VzdCBkYXRhc2V0IGlzIDI3IE1CLiBJZiBJJ20gZ29pbmcgdG8gZnJlZSB1cCA5NiBNQiwgd2VsbCBJIGp1c3QgZG9uJ3QgaGF2ZSBpdC4gSSBndWVzIEkgbmVlZCBldmVuIGZld2VyIGVkZ2VzLiANCg0KT2theSwgdGhlIGNvbnRlbnQgb2YgdGhlIHZvdGVzIGlzIG5leHQuIFdoYXQgYXJlIHRoZSBkaWZmZXJlbnQgY2F0ZWdvcmllcz8gV2hpY2ggYXJlIGxpa2VseSB0byBoYXZlIHNvbWUgdmFyaWF0aW9uPyANCg0KYGBge3J9DQpqYW5pdG9yOjp0YWJ5bChpc3N1ZXMsIGlzc3VlKQ0KYGBgDQoNClN1cnByaXNpbmdseSwgIkh1bWFuIHJpZ2h0cyIgaXMgb25lIG9mIHRoZSBtb3JlIGNvbnRyb3ZlcnNpYWwuDQoNCmBgYHtyfQ0KbmV0IDwtIHVudm90ZXMgJT4lIA0KICBsZWZ0X2pvaW4oc2VsZWN0KHJvbGxfY2FsbHMsIHJjaWQsIGltcG9ydGFudHZvdGUsIGRhdGUpLCBieSA9ICdyY2lkJykgJT4lIA0KICBtdXRhdGUoeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihkYXRlKSkgJT4lIA0KICBsZWZ0X2pvaW4oaXNzdWVzLCBieSA9ICJyY2lkIikgJT4lIA0KICBmaWx0ZXIoeWVhciAlaW4lIHNlcSgxOTgwLCAyMDIwLCAxMCkgJiANCiAgICAgICAgIGltcG9ydGFudHZvdGUgPT0gMSAmIA0KICAgICAgICAgaXNzdWUgPT0gIkh1bWFuIHJpZ2h0cyIpDQoNCg0KIyBWZWN0b3Igb2YgT0VDRCBjb3VudHJpZXMgaW4gSVNPLTIgY2hhcmFjdGVyDQpvZWNkIDwtIHJlYWRfY3N2KCdvZWNkLmNzdicpICU+JSANCiAgcHVsbChMT0NBVElPTikgJT4lIHVuaXF1ZSgpICU+JSANCiAgY291bnRyeWNvZGUoImlzbzNjIiwgImlzbzJjIikNCg0Kb2VjZCA8LSBvZWNkWyFpcy5uYShvZWNkKV0NCg0KbmV0IDwtIGZpbHRlcihuZXQsIGNvdW50cnlfY29kZSAlaW4lIG9lY2QpDQoNCg0KIyB1bmRpcmVjdGVkIGdyYXBoIGRhdGEgLS0tLS0tLS0NCmZ1bGxfbmV0IDwtIG5ldCAlPiUNCiAgZ3JvdXBfYnlfYWxsKCkgJT4lDQogIHN1bW1hcml6ZShvdGhlcl9jb3VudHJ5ID0gbmV0JGNvdW50cnlbbmV0JGNvdW50cnkgPiBjb3VudHJ5ICYgbmV0JHJjaWQgPT0gcmNpZF0sIA0KICAgICAgICAgICAgb3RoZXJfcmNpZCA9IG5ldCRyY2lkW25ldCRjb3VudHJ5ID4gY291bnRyeSAmIG5ldCRyY2lkID09IHJjaWRdLA0KICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGxlZnRfam9pbihzZWxlY3QobmV0LCBjb3VudHJ5LCByY2lkLCB2b3RlKSwgYnkgPSBjKCJvdGhlcl9jb3VudHJ5IiA9ICJjb3VudHJ5IiwgIm90aGVyX3JjaWQiID0gInJjaWQiKSkgJT4lDQogIHJlbmFtZSh2b3RlID0gdm90ZS54LCBvdGhlcl92b3RlID0gdm90ZS55KSAlPiUNCiAgZmlsdGVyKHZvdGUgPT0gb3RoZXJfdm90ZSkgJT4lDQogIHNlbGVjdChjb3VudHJ5LCBvdGhlcl9jb3VudHJ5LCB2b3RlLCBldmVyeXRoaW5nKCkpDQoNCm5ldF92ZXJ0aWNlcyA8LSBuZXQgJT4lIA0KICBzZWxlY3QoY291bnRyeSwgY291bnRyeV9jb2RlKSAlPiUgdW5pcXVlKCkNCg0KbmV0X2dyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShmdWxsX25ldCwgZGlyZWN0ZWQgPSBGQUxTRSwgdmVydGljZXMgPSBuZXRfdmVydGljZXMpDQoNCmdncmFwaChuZXRfZ3JhcGgsIGxheW91dCA9ICdzdHJlc3MnKSArIA0KICBnZW9tX2VkZ2VfZGlhZ29uYWwoYWVzKGVkZ2VfY29sb3VyID0gdm90ZSkpDQpgYGANCg0KVGhlcmUgYXJlIG5vdyAxNiB2b3RlcyBhY3Jvc3MgMTk5MC0yMDIwIGRlYWxpbmcgd2l0aCBodW1hbiByaWdodHMuIFRoZXJlIHdlcmUgNjA5IHZvdGVzIGNhc3QgYW1vbmcgMzgtaXNoIGNvdW50cmllcywgYW5kIHN0aWxsIEknbSBnZXR0aW5nIHRvbyBtYW55IG9ic2VydmF0aW9ucy4gQW5kIHRoZSByZWFzb24gaXMgb2J2aW91cy4gSSdtIHVzaW5nIGFsbCB0aGUgZGF0YSBldmVyeSB0aW1lLiBPb3BzLiBPa2F5LCBvYnNlcnZhdGlvbnMgcmVkdWNlZCB0byBvbmx5IDgwMDAsIHdoaWNoIG1ha2VzIHNlbnNlLiANCg0KDQpUaGF0J3MgSSdsbCBJIGhhdmUgdGltZSBmb3IgdGhpcyBtb3JuaW5nLiBIb3BlZnVsbHkgdG9tb3Jyb3cgSSBjYW4gbWFrZSBhIGxpdHRsZSBtb3JlIHByb2dyZXNzLiANCg0KLS0tDQpDaGFybGllIEdhbGxhZ2hlciwgMjAyMQ==